<template>
  <!--
    Cesium 容器组件
    - 提供一个承载 Cesium Viewer 的容器，并通过 provide 向子组件注入 viewer 实例
    - 子组件（如折线、面、点等）可以通过 inject 获取 viewer 并进行实体的添加与移除
  -->
  <div class="viewer-wrap">
    <div ref="containerRef" class="cesium-container"></div>
    <!--
      具名插槽 entities：用于承载各种实体组件（例如 PolylineEntity 等）
      这些子组件会在父组件初始化 viewer 后，通过 inject 获取到 viewer 并完成各自绘制
    -->
    <slot name="entities" />
  </div>
  <!-- 可选：用于展示调试信息或额外内容的默认插槽 -->
  <slot />
  
</template>

<script setup>
// 中文注释：
// - 职责：创建并管理一个 Cesium.Viewer 实例，通过 provide 向子组件共享 viewer 引用
// - 依赖：需在入口处引入 "cesium/Build/Cesium/Widgets/widgets.css"，并设置 window.CESIUM_BASE_URL
// - 用法：父组件包裹后，在 #entities 插槽中放置实体组件（点/线/面/模型等）

import { onMounted, onBeforeUnmount, provide, ref, watch, nextTick } from "vue";
import * as Cesium from "cesium";

// props 设计：
// - options：Cesium.Viewer 构造参数（控制底图、地形、UI面板等）
// - camera：初始相机视角设置（经纬高与姿态）
// - accessToken：可选的 Cesium ion 令牌，使用 ion 服务时需要
// - homeZoomOutFactor：回到整球视角后再额外缩小的倍数（让地球更小）
const props = defineProps({
  // Viewer 构造参数
  options: {
    type: Object,
    default: () => ({
      // 常用默认参数（根据需要可调整）
      animation: false, // 动画面板
      baseLayerPicker: false, // 底图选择器
      geocoder: false, // 地名搜索
      timeline: false, // 时间轴
      fullscreenButton: false, // 全屏按钮
      homeButton: false, // Home 视角按钮
      navigationHelpButton: false, // 帮助按钮
      sceneModePicker: false, // 2D/3D 切换
      infoBox: false, // 信息框
      selectionIndicator: false, // 选中指示器
      // 默认使用 OSM 底图（无需密钥，通常可用）
      imageryProvider: new Cesium.OpenStreetMapImageryProvider(),
      // 指定椭球地形，避免默认请求 ion 地形导致报错
      terrainProvider: new Cesium.EllipsoidTerrainProvider(),
    }),
  },
  // 初始相机参数（经度、纬度、高度、heading、pitch、roll）单位：度、米
  camera: {
    type: Object,
    default: () => ({
      lon: 116.39, // 北京经度
      lat: 39.9, // 北京纬度
      height: 1500000,
      heading: 0,
      pitch: -45,
      roll: 0,
    }),
  },
  // 可选：Cesium ion 访问令牌，若不传将使用非 ion 的默认底图避免英文提示
  accessToken: {
    type: String,
    default: "",
  },
  // 进入页面后在 Home 视角基础上再后退的倍数（地球更小）。
  // 实际偏移量 = WGS84 最大半径 * 该系数
  homeZoomOutFactor: {
    type: Number,
    default: 0, // 0 表示不额外缩小
  },
});

// 变量：Dom 容器引用（承载 Cesium 画布）
const containerRef = ref(null);
// 变量：Viewer 实例引用（提供给子组件）
const viewerRef = ref(null);

// 向子组件提供 key，用于 inject 获取 viewer 实例（通过 ref 提供，子组件可在 viewerRef.value 可用后再使用）
const CESIUM_VIEWER_KEY = "cesiumViewer";
provide(CESIUM_VIEWER_KEY, viewerRef);

onMounted(() => {
  // 优先设置 ion token（若提供）
  if (props.accessToken) {
    Cesium.Ion.defaultAccessToken = props.accessToken;
  }
  // 设置全局 Home 视角为整球，并确保初始就看到完整地球
  Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(-180, -90, 180, 90);
  Cesium.Camera.DEFAULT_VIEW_FACTOR = 0.0;
  // 1) 创建 Viewer 实例（将内置默认项与外部传入的 options 合并，避免外部覆盖导致 infoBox 等恢复默认）
  const baseOptions = {
    animation: false,
    baseLayerPicker: false,
    geocoder: false,
    timeline: false,
    fullscreenButton: false,
    homeButton: false,
    navigationHelpButton: false,
    sceneModePicker: false,
    infoBox: false,
    selectionIndicator: false,
    imageryProvider: new Cesium.OpenStreetMapImageryProvider(),
    terrainProvider: new Cesium.EllipsoidTerrainProvider(),
  };
  const mergedOptions = Object.assign({}, baseOptions, props.options || {});
  viewerRef.value = new Cesium.Viewer(containerRef.value, mergedOptions);

  // 2) 初始直接回到完整地球（避免需要滚轮缩小）
  try {
    viewerRef.value.scene.camera.flyHome(0.0);
  } catch (_) {}
  // 在完整地球基础上再缩小一点（让地球更小）
  try {
    const amount = Cesium.Ellipsoid.WGS84.maximumRadius * (props.homeZoomOutFactor || 0);
    if (amount > 0) {
      viewerRef.value.scene.camera.zoomOut(amount);
    }
  } catch (_) {}

  // 3) viewerRef 已通过 provide 暴露，这里仅赋值即可

  // 4) 降低资源依赖，避免某些环境下因图片资源解码失败导致渲染停止
  const viewer = viewerRef.value;
  if (viewer) {
    // 避免地形深度测试遮挡导致细线/垂直线不可见
    try { viewer.scene.globe.depthTestAgainstTerrain = false; } catch (_) {}
    // 关闭天空盒，避免加载天空盒贴图
    viewer.scene.skyBox = undefined;
    // 设置背景色，避免黑屏无背景
    viewer.scene.backgroundColor = Cesium.Color.fromCssColorString("#0b1e39");
    // 打开错误监听，出现渲染错误时不中断渲染
    viewer.scene.renderError.addEventListener((err) => {
      console.error("渲染错误：", err);
      try {
        viewer.render();
      } catch (_) {
        // 忽略
      }
    });
    // 容器尺寸变化时，强制调整画布尺寸，避免显示不全
    // 变量：用于监听容器尺寸变化的 ResizeObserver
    const ro = new ResizeObserver(() => {
      try {
        viewer.resize();
        viewer.requestRender();
      } catch (_) {}
    });
    if (containerRef.value) ro.observe(containerRef.value);
    // 首次渲染后做一次延迟 resize，确保填满
    nextTick(() => {
      try {
        viewer.resize();
        viewer.requestRender();
      } catch (_) {}
    });
    // 保存以便销毁
    viewer._resizeObserver = ro;
  }
});

onBeforeUnmount(() => {
  // 组件销毁时，销毁 Cesium 资源，避免内存泄漏
  if (viewerRef.value) {
    // 释放 ResizeObserver
    if (viewerRef.value._resizeObserver) {
      try {
        viewerRef.value._resizeObserver.disconnect();
      } catch (_) {}
      viewerRef.value._resizeObserver = null;
    }
    viewerRef.value.destroy();
    viewerRef.value = null;
  }
});

// 监听 camera 变化，动态飞行到新的视角
watch(
  () => props.camera,
  (val) => {
    if (viewerRef.value && val) {
      flyToCamera(val);
    }
  },
  { deep: true }
);

// 工具函数：根据经纬度与姿态飞行到指定视角
function flyToCamera(camera) {
  const { lon, lat, height, heading, pitch, roll } = camera;
  const destination = Cesium.Cartesian3.fromDegrees(lon, lat, height ?? 1000);
  const hpr = new Cesium.HeadingPitchRoll(
    Cesium.Math.toRadians(heading ?? 0),
    Cesium.Math.toRadians(pitch ?? -45),
    Cesium.Math.toRadians(roll ?? 0)
  );
  viewerRef.value.camera.flyTo({
    destination,
    orientation: {
      heading: hpr.heading,
      pitch: hpr.pitch,
      roll: hpr.roll,
    },
    duration: 1.2,
  });
}

// 对外暴露方法，方便父组件通过 ref 调用（可选）
// 暴露方法：允许父级通过 ref 获取 viewer 实例
defineExpose({ getViewer: () => viewerRef.value });
</script>

<style scoped>
.viewer-wrap {
  position: relative;
  width: 100%;
  height: 100%;
}
.cesium-container {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
}
</style>


